package tech.xujian.lock.distributed.lockdistributed.interceptor;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import tech.xujian.lock.distributed.lockdistributed.annotation.RedisLock;
import tech.xujian.lock.distributed.lockdistributed.annotation.RedisLockType;
import tech.xujian.lock.distributed.lockdistributed.service.RedisService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
@Slf4j
public class RedisLockInterceptor implements HandlerInterceptor {

    @Autowired
    RedisService redisService;

    private static final String CACHE_KEY_REDIS_LOCK = "system:distributed:lock:";

    //访问前拦截枷锁，锁定时抛出异常，由全局异常捕获
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            RedisLock redisLock = handlerMethod.getMethodAnnotation(RedisLock.class);
            if(redisLock == null){
                return HandlerInterceptor.super.preHandle(request, response, handler);
            }
            //需要进行锁判断
            switch (redisLock.type()){
                case IP:
                    doIpLock(request);
                    break;
                case USERNAME:
                    //TODO: 从request中通过header等读取到用户信息，然后参考doIpLock实现
                    //略
                    break;
                case COUNT:
                    //某个方法同时访问的人数限制的实现
                    doCountLock(handlerMethod);
                    break;
                case ANY:
                    //TODO: 该方法同时只允许一个人访问，实现方法与doCountLock一致
                    //略
                    break;
            }
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    //访问结束后释放锁
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        if(handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            RedisLock redisLock = handlerMethod.getMethodAnnotation(RedisLock.class);
            if(redisLock == null){
                HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
                return;
            }
            //需要进行锁判断
            switch (redisLock.type()){
                case IP:
                    releaseIpLock(request);
                    break;
                case USERNAME:
                    //TODO: 略，释放锁
                    break;
                case COUNT:
                    //某个方法同时访问的人数限制的实现
                    releaseCountLock(handlerMethod);
                    break;
                case ANY:
                    //TODO: 略，释放锁
                    break;
            }
        }
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }

    //访问数量限制lock
    private void doCountLock(HandlerMethod handlerMethod){
        //根据方法名进行锁定
        String redisKey = CACHE_KEY_REDIS_LOCK + "count:" + handlerMethod.getMethod().getDeclaringClass() + ":" + handlerMethod.getMethod().toGenericString();
        redisKey = redisKey.replaceAll(" ","");
        log.info(redisKey);
        long countNow = redisService.getLong(redisKey);
        log.info("当前方法访问人数：" + countNow);
        //设限制100人，也可以考虑在注解中设置
        Assert.isTrue(countNow < 10,"系统拥堵，请稍后重试！当前访问人数：" + countNow);

        redisService.increment(redisKey);
    }

    private void releaseCountLock(HandlerMethod handlerMethod) {
        String redisKey = CACHE_KEY_REDIS_LOCK + "count:" + handlerMethod.getMethod().getDeclaringClass() + ":" + handlerMethod.getMethod().toGenericString();
        redisKey = redisKey.replaceAll(" ","");
        long countNow = redisService.decrement(redisKey);
        log.info("当前方法访问人数：" + countNow);
    }

    //ip判断和lock
    private void doIpLock(HttpServletRequest request){
        //如果是IP锁，则到redis中读取是否已经存在key
        String ip = ServletUtil.getClientIP(request);
        String redisKey = CACHE_KEY_REDIS_LOCK + "ip:" + ip;
        String value = redisService.get(redisKey);
        if(StrUtil.isEmpty(value)){
            //redis自然过期时间为1分钟，即为在一分钟内完成的请求会自动解锁，也可以考虑设置得更短
            redisService.set(redisKey,ip,1);
            return;
        }else{
            throw new RuntimeException("操作太快，请稍后重试");
        }
    }


    //释放锁
    private void releaseIpLock(HttpServletRequest request) {
        String ip = ServletUtil.getClientIP(request);
        String redisKey = CACHE_KEY_REDIS_LOCK + "ip:" + ip;
        redisService.getAndDelete(redisKey);
    }

}
